#include "sleeqhash_bdb.h"
#include <QDataStream>
#include <QByteArray>
#include <QDebug>
#include <QDate>
//Версия 0.4 от 30 мая 2011
// добавлено - cmp_dup_sort
// исправлено - при удалении записи через курсор, оставались запсии в базе вторичных индексов (причина не известна) Только для String
// неисправлено - та же хрень, при записи в базу с конкретным ключем. Индексы раздваиваются (если новый индекс больше)
//приняторешение, предварительно старую запись удалять.
//22 июня  2011
//SLEE_HDB_REC вместо define теперь typedef


//------------------------------------------------------------------------------------------------------------

QByteArray SleeQHash_BDB::gm_qb_buffer;

//------------------------------------------------------------------------------------------------------------

SleeQHash_BDB::SleeQHash_BDB():
    mb_dup_support(false)
{
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::qh_to_qb(QByteArray &buffer, const SLEE_HDB_REC &hash)
{
    buffer.clear();
    QDataStream toBuf(&buffer, QIODevice::WriteOnly);
    toBuf<<hash;
}

//------------------------------------------------------------------------------------------------------------


void SleeQHash_BDB::qb_to_qh(QByteArray &buffer, SLEE_HDB_REC &hash)
{
    QDataStream fromBuf(&buffer,QIODevice::ReadOnly);
    hash.clear();
    fromBuf>>hash;
}



//------------------------------------------------------------------------------------------------------------


bool SleeQHash_BDB::isRelevant(QVariant &qv_data, QVariant &qv_request)
{
    if(qv_data.type() != qv_request.type()) return false;
    switch(qv_data.type())
    {
    case QVariant::String:
        if(qv_data.toString().indexOf(qv_request.toString(), 0, Qt::CaseInsensitive) == 0) return true;
        return false;
    case QVariant::Int:
        if(qv_data.toInt() == qv_request.toInt()) return true;
        return false;
    case QVariant::Double:
        if(qv_data.toDouble() == qv_request.toDouble()) return true;
        return false;
    case QVariant::Date:
        if(qv_data.toDate() == qv_request.toDate()) return true;
        return false;
    default:
        qv_data.convert(QVariant::ByteArray);
        qv_request.convert(QVariant::ByteArray);
        return qv_data.toByteArray().contains(qv_request.toByteArray());
        break;
    }
    return false;
}

//------------------------------------------------------------------------------------------------------------


int SleeQHash_BDB::secondary_key_extractor(Db *sDb, const Dbt *pkey, const Dbt *pdata, Dbt *skey)
{
   SecDb *p_secDb = dynamic_cast<SecDb *>(sDb);
   if(isDbtSpec(pkey))
   {
       skey->set_data(pkey->get_data());
       skey->set_size(pkey->get_size());
       return 0;
   }
   int indexFld = p_secDb->indx;
   SLEE_HDB_REC qh_data;
   QVariant qv_skey;
   QByteArray qb_buf;
   dbt_to_qb(qb_buf, pdata);
   qb_to_qh(qb_buf, qh_data);

   qv_skey = qh_data[indexFld];
   gm_qb_buffer.clear();
   qv_to_qb(gm_qb_buffer, qv_skey);
   qb_to_dbt(gm_qb_buffer,skey);
   //qDebug()<<"sec key extractor. sec key is"<<qv_skey;
   return 0;
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::pre_initDb()
{
   qDebug()<<"Slee Hash Berkeley DB is loading...version 0.4 @30.05.11";
    if(m_hashIndexDb.isEmpty())
    {
        //m_pDb->set_flags(DB_DUP | DB_DUPSORT);
       // mb_dup_support = true;
    }
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::post_initDb()
{
  if(m_hashIndexDb.isEmpty()) return;
  QString strIndxDbName;
  try
  {
    foreach(int indx, m_hashIndexDb.keys())
    {
        strIndxDbName.clear();
        if(m_hashIndexDb[indx] == NULL)
            m_hashIndexDb[indx] = new SecDb(NULL,0);
        strIndxDbName.sprintf("%d",indx);
        QString qstrDbFilePathName = m_qdirHome.absolutePath() + QDir::separator() + m_qstrDbName + "i" + strIndxDbName + ".db";

        m_hashIndexDb[indx]->set_flags(DB_DUPSORT);
        m_hashIndexDb[indx]->set_bt_compare(cmpr_var_keys);
        m_hashIndexDb[indx]->set_dup_compare(cmpr_dup_sort);

        m_hashIndexDb[indx]->open(NULL, qstrDbFilePathName.toStdString().c_str(),
                                  strIndxDbName.toStdString().c_str(), DB_BTREE, m_dbOpenFlags, 0);
        m_hashIndexDb[indx]->indx = indx;
        m_pDb->associate(NULL, m_hashIndexDb[indx],secondary_key_extractor,0);
        qDebug()<<"index db set"<<qstrDbFilePathName;
    }

  }
  catch(DbException &e)
  {
      qCritical()<<"SleeQHash_BDB::post_initDb() has caught an Db Exception."<<e.what();
  }
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::pre_closeDb()
{

    foreach(int indx, m_hashIndexDb.keys())
    {
        m_hashIndexDb[indx]->close(0);
        delete m_hashIndexDb[indx];
        m_hashIndexDb[indx] = NULL;
    }
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::setupFields(const QStringList &lstNames, const QList<QVariant::Type> &fld_types, const QList<int> &lstIndexFlds)
{
    m_hashIndexDb.clear();
    if(lstNames.size() != fld_types.size())
    {
        qWarning()<<"fields names count is not equal fields types count";
        return;
    }
    for(int i = 0; i<lstIndexFlds.size(); i++)
    {
        m_hashIndexDb[lstIndexFlds.at(i)] = NULL;
    }
    m_fld_names = lstNames;
    m_fld_types = fld_types;
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::syncDb()
{
    try
    {
        m_pDb->sync(0);
        foreach(Db *pDbI, m_hashIndexDb)
            if(pDbI != NULL) pDbI->sync(0);
    }
    catch(DbException &e)
    {
        qCritical()<<"SleeQHash_BDB::syncDb() has caught a DbException"<<e.what();
    }
}

//------------------------------------------------------------------------------------------------------------

bool SleeQHash_BDB::is_valid_data(const SLEE_HDB_REC &qh_data)
{
    QList<int> data_keys = qh_data.keys();
    if(data_keys.size() != m_fld_types.size())
    {
       qWarning()<<"SleeQHash_BDB::is_valid_data(). data has wrong fields number";
       qWarning()<<"expected data size"<<m_fld_types.size();
        return false;
    }
    for(int i = 0; i < m_fld_types.size(); i++)
    {
        if(!data_keys.contains(i))
        {
            qWarning()<<"SleeQHash_BDB::is_valid_data() field"<<i<<"is not present in data";
            return false;
        }
        if(qh_data.value(i).type() != m_fld_types.at(i))
        {
            qWarning()<<"SleeQHash_BDB::is_valid_data() types mistmatch. type"<<QVariant::typeToName(m_fld_types.at(i))<<"expected. got"<<QVariant::typeToName(qh_data.value(i).type());
            return false;
        }
    }
    return true;
}

//------------------------------------------------------------------------------------------------------------

QVariant SleeQHash_BDB::putData(const SLEE_HDB_REC &qh_data, const QVariant *pqv_key)
{
    if(!m_bDbOk) return 0;
    QVariant qv_key;
    QByteArray qb_key_buf;
    QByteArray qb_buf;
    if(pqv_key == NULL)
    {
        qv_key = genId();
        if(qv_key.isNull())
        {
            qWarning()<<"null key generated";
            return qv_key;
        }
    }
    else
    {
        qv_key = *pqv_key;
    }
    if(!match_key_type(qv_key))
    {
       qWarning()<<"matching keytype failed";
        return QVariant();
    }

    Dbt key, data;
    qv_to_dbt(qb_key_buf, qv_key, key);

    qh_to_qb(qb_buf, qh_data);
    qb_to_dbt(qb_buf,data);
    try
    {
        m_pDb->put(NULL,&key, &data, 0);
    }
    catch(DbException &e)
    {
        qCritical()<<"SleeQHash_BDB::putData() has caught a Db Exception"<<e.what();
        return QVariant();
    }
    syncDb();
    return qv_key;
}

//------------------------------------------------------------------------------------------------------------

int SleeQHash_BDB::getData(SLEE_HDB_REC_LST &lst_data, const QVariant &qv_key)
{
    if(!m_bDbOk) return -1;
    Dbt key, data;
    QByteArray qb_key_buffer;
    QByteArray qb_buf;
    SLEE_HDB_REC qh_data;
    qv_to_dbt(qb_key_buffer, qv_key, key);
    Dbc *pC;
    m_pDb->cursor(NULL, &pC, 0);

    int ret;
    u_int32_t get_flag = DB_SET;
    try
    {
        while(1)
        {
            //ret = m_pDb->get(NULL, &key, &data, 0);
            ret = pC->get(&key, &data, get_flag);
            if(ret) break;
            if(mb_dup_support)
                get_flag = DB_NEXT_DUP;
            dbt_to_qb(qb_buf, data);
            qb_to_qh(qb_buf, qh_data);
            lst_data.append(qh_data);
            if(!mb_dup_support) break;
        }
        pC->close();
    }
    catch(DbException &e)
    {
        qCritical()<<"SleeQHash_BDB::getData() has caught Db Exception"<<QString::fromUtf8(e.what());
        return -1;
    }
    if(ret == DB_NOTFOUND) return 1;
    return 0;
}

//------------------------------------------------------------------------------------------------------------

int SleeQHash_BDB::removeData(const QHash<int, QVariant> &qh_data, const QVariant &qv_pkey)
{
   Q_UNUSED(qh_data);
    if(!m_bDbOk) return -1;
    Dbt key/*, data*/;
    QByteArray qb_key_buffer/*, qb_data_buffer*/;
    int ret;
//    qh_to_qb(qb_data_buffer, qh_data);
//    qb_to_dbt(qb_data_buffer, data);
    qv_to_dbt(qb_key_buffer,qv_pkey,key);
//    qDebug()<<"SleeQHash_BDB. removing data"<<qv_pkey;
//    Dbc *pC;
//    m_pDb->cursor(NULL,&pC,0);
//    ret = pC->get(&key, &data, DB_GET_BOTH);
//    if(ret) return 1;
//    ret = pC->del(0);
//    if(ret) return 1;
//    pC->close();
    ret = m_pDb->del(NULL, &key,0);
    if(ret) return 1;
    syncDb();
    return 0;
}


//------------------------------------------------------------------------------------------------------------

QVariantList SleeQHash_BDB::findRelevantData(int i_fld, QVariant &qv_request, SLEE_HDB_REC_LST *pqh_data_lst, SleeQHash_Constrain *p_contsrain)
{
    QVariantList lst_qv_pkeys;
    if(!m_bDbOk) return lst_qv_pkeys;
    if(i_fld != PRIM_FLD)
        if(!m_hashIndexDb.contains(i_fld)) return lst_qv_pkeys;
    if(!qv_request.isValid()) return lst_qv_pkeys;
    int ret;
    QVariant qv_pkey;
    Dbc *pC;
    Db *pDb;
    if(i_fld == PRIM_FLD)
        pDb = m_pDb;
    else
        pDb = m_hashIndexDb[i_fld];
    Dbt key, pkey, data;
    QVariant qv_key;
    u_int32_t searchFlag = DB_SET_RANGE;
    QByteArray qb_key_buffer;
    QByteArray qb_data_buffer;
    SLEE_HDB_REC qh_data;
    try
    {
        pDb->cursor(NULL, &pC, 0);
        while(1)
        {
            qv_to_dbt(qb_key_buffer, qv_request, key);
            if(i_fld != PRIM_FLD)
            {
                ret = pC->pget(&key, &pkey, &data, searchFlag);
            }
            else
            {
                ret = pC->get(&key,&data, searchFlag);
                pkey.set_data(key.get_data());
                pkey.set_size(key.get_size());
            }
            if(ret)
            {
                pC->close();
                return lst_qv_pkeys;
            }

            dbt_to_qv(key, qv_key);
            if(!isRelevant(qv_key, qv_request)) break;

            dbt_to_qb(qb_data_buffer, data);
            qb_to_qh(qb_data_buffer,qh_data);

            searchFlag = DB_NEXT;

            if(p_contsrain != NULL)
            {
                if(!p_contsrain->process_data(qh_data)) continue;
            }
            if(pqh_data_lst != NULL)
            {

                pqh_data_lst->append(qh_data);
            }

            dbt_to_qv(pkey,qv_pkey);
            lst_qv_pkeys.append(qv_pkey);

        }

    }
    catch (DbException &e)
    {
        qCritical()<<"SleeQHash_BDB::findRelevantData() has caught a Db Exception"<<e.what();
    }
    pC->close();
    return lst_qv_pkeys;
}

//------------------------------------------------------------------------------------------------------------

QVariantList SleeQHash_BDB::findDataRange(int i_fld, QVariant *pqv_min, bool do_eq_min , QVariant *pqv_max, bool do_eq_max,
                                          SLEE_HDB_REC_LST *pqh_data_lst, SleeQHash_Constrain *p_contsrain)
{
    QVariantList lst_pkeys;
    if(!m_bDbOk) return lst_pkeys;
    if(i_fld != PRIM_FLD)
        if(!m_hashIndexDb.contains(i_fld)) return lst_pkeys;
    QVariant qv_min, qv_max;
    if(pqv_min != NULL)
    {
        if(!pqv_min->isValid()) return lst_pkeys;
        qv_min = *pqv_min;
        if(i_fld<0) match_key_type(qv_min);
    }
    if(pqv_max != NULL)
    {
        if(!pqv_max->isValid()) return lst_pkeys;
        qv_max = *pqv_max;
        if(i_fld<0) match_key_type(qv_max);
    }

    int ret;
    QVariant qv_pkey;

    Dbc *pC = NULL, *pC_max = NULL;
    Db *pDb;

    if(i_fld == PRIM_FLD)
        pDb = m_pDb;
    else
        pDb = dynamic_cast<Db *>(m_hashIndexDb[i_fld]);

    QVariant qv_key;
    bool b_check_max = false;
    Dbt key_min, key_max, pkey;
    QByteArray qb_keymin_buffer, qb_keymax_buffer, qb_data_buffer;
    SLEE_HDB_REC    qh_data;
    Dbt data;
    u_int32_t search_flag  = DB_NEXT;
    if(pqv_min != NULL)
    {
        qv_to_dbt(qb_keymin_buffer, qv_min,key_min);
        search_flag  = DB_SET_RANGE;
    }
    qDebug()<<"find data range min/max"<<qv_min<<qv_max;
    try
    {
        if(pqv_max != NULL)
        {
            qv_to_dbt(qb_keymax_buffer, qv_max, key_max);
            pDb->cursor(NULL, &pC_max, 0);
            ret = pC_max->get(&key_max, &data, DB_SET_RANGE);
            pC_max->close();
            if(ret > 0) return lst_pkeys;
            if(!ret)
                b_check_max = true;
        }
        pDb->cursor(NULL, &pC, 0);
        if(!do_eq_min)
        {
            if(i_fld != PRIM_FLD)
            {
                ret = pC->pget(&key_min, &pkey, &data, search_flag);
            }
            else
            {
                ret = pC->get(&key_min,&data, search_flag);
                pkey.set_data(key_min.get_data());
                pkey.set_size(key_min.get_size());
            }
            if(ret)
            {
                pC->close();
                return lst_pkeys;
            }
            search_flag = DB_NEXT;
        }
        while(1)
        {

            if(i_fld != PRIM_FLD)
            {
                ret = pC->pget(&key_min, &pkey, &data, search_flag);
            }
            else
            {
                ret = pC->get(&key_min,&data, search_flag);
                pkey.set_data(key_min.get_data());
                pkey.set_size(key_min.get_size());
            }
            if(ret) return lst_pkeys;
            if(b_check_max)
            {
               ret = cmpr_var_keys(pDb, &key_min, &key_max);
               if((do_eq_max) && (ret > 0)) break;
               if((!do_eq_max) && (ret >= 0)) break;
            }

            dbt_to_qb(qb_data_buffer, data);
            qb_to_qh(qb_data_buffer,qh_data);

            search_flag = DB_NEXT;

            if(p_contsrain != NULL)
            {
                if(!p_contsrain->process_data(qh_data))
                {
                    //qDebug()<<"cutted data"<<qh_data;
                    continue;
                }
            }

            if(pqh_data_lst != NULL)
            {
                pqh_data_lst->append(qh_data);
            }
            dbt_to_qv(key_min, qv_key);
            dbt_to_qv(pkey, qv_pkey);
            lst_pkeys.append(qv_pkey);

        }
    }
    catch(DbException &e)
    {
        qCritical()<<"SleeQHash_BDB::findDataRage() has caught a Db Exception"<<e.what();
    }
    pC->close();
    return lst_pkeys;
}



//------------------------------------------------------------------------------------------------------------

QVariantList SleeQHash_BDB::findDataEq(int i_fld, const QVariant &qv_request, SLEE_HDB_REC_LST *pqh_data_lst, SleeQHash_Constrain *p_contsrain)
{
    QVariantList lst_pkeys;
    if(!m_bDbOk) return lst_pkeys;
    if(!m_hashIndexDb.contains(i_fld) && (i_fld >=0)) return lst_pkeys;
    QVariant qv_req = qv_request;
    if(i_fld == PRIM_FLD) match_key_type(qv_req);
    int ret;
    QVariant qv_pkey;
    QByteArray qb_buffer, qb_data_buffer;
    SLEE_HDB_REC    qh_data;
    Dbc *pC = NULL;

    Db *pDb;
    if(i_fld != PRIM_FLD)
        pDb = dynamic_cast<Db *>(m_hashIndexDb[i_fld]);
    else
        pDb = m_pDb;

    QVariant qv_key;
    pDb->cursor(NULL, &pC, 0);
    Dbt data, key, pkey;
    u_int32_t searchflag;
    if(!qv_req.isNull())
    {
        qv_to_dbt(qb_buffer, qv_req, key);
        searchflag = DB_SET;
    }
    else
    {
        searchflag = DB_NEXT;
    }
    while(1)
    {
        if(i_fld != PRIM_FLD)
            ret = pC->pget(&key, &pkey, &data, searchflag);
        else
        {
            ret = pC->get(&key,&data, searchflag);
            pkey.set_data(key.get_data());
            pkey.set_size(key.get_size());
        }
        if(ret) break;
        if(isDbtSpec(&pkey)) continue;
        if(searchflag == DB_SET)
        {
            searchflag = DB_NEXT_DUP;
        }
        dbt_to_qb(qb_data_buffer, data);
        qb_to_qh(qb_data_buffer,qh_data);

        if(p_contsrain != NULL)
        {
            if(!p_contsrain->process_data(qh_data)) continue;
        }

        if(pqh_data_lst != NULL)
        {

            pqh_data_lst->append(qh_data);
        }

        dbt_to_qv(key, qv_key);
        dbt_to_qv(pkey,qv_pkey);
        lst_pkeys.append(qv_pkey);
    }
    pC->close();
    return lst_pkeys;
}


//------------------------------------------------------------------------------------------------------------

//TODO check for recovery aspect
void SleeQHash_BDB::recovery_db()
{
    qDebug()<<"start_recovery";
    closeDb();
    Db old_db(NULL,0);
    int ret;
    QString qstrDbFilePathName = m_qdirHome.absolutePath() + QDir::separator() + m_qstrDbName+".db";
    QString qstr_old_db_file = qstrDbFilePathName+"~";
    QFile::rename(qstrDbFilePathName, qstr_old_db_file);

    ret = old_db.open(NULL,qstr_old_db_file.toStdString().c_str(), m_qstrDbName.toStdString().c_str(), DB_BTREE, DB_RDONLY, 0);
    if(ret)
    {
        qDebug()<<"unable to open db";
        return;
    }
    QString strIndxDbName;
    foreach(int indx, m_hashIndexDb.keys())
    {
        strIndxDbName.sprintf("%d",indx);
        qstrDbFilePathName = m_qdirHome.absolutePath() + QDir::separator() + m_qstrDbName + "_i" + strIndxDbName + ".db";
        QFile::remove(qstrDbFilePathName);
        qDebug()<<"old index file"<<qstrDbFilePathName<<"deleted";
    }
    SLEE_HDB_REC qh_data;
    QByteArray qb_data;
    initDb(m_qdirHome.absolutePath() ,m_qstrDbName);
    QVariant   qv_old_key;
    Dbc *pC;
    old_db.cursor(NULL, &pC, 0);
    Dbt data, key;
    while(1)
    {
        qDebug()<<"recovery read process";
        ret = pC->get(&key, &data, DB_NEXT);
        if(ret == DB_NOTFOUND)
        {
          qDebug()<<"recovery read process is done ret ="<<"DB_NOTOUND";
            break;
        }
        if(ret)
        {
          qDebug()<<"recovery read process is done ret ="<<ret;
            break;
        }
        if(isDbtSpec(&key))
        {
            u_int64_t id_data;
            memcpy(&id_data, data.get_data(), sizeof(u_int64_t));
            setId(id_data);
            continue;
        }
        dbt_to_qv(key,qv_old_key);
        dbt_to_qb(qb_data, data);
        qb_to_qh(qb_data, qh_data);

        putData(qh_data, &qv_old_key);

    }
    pC->close();
    old_db.close(0);
    QFile::remove(qstr_old_db_file);
    qDebug()<<"recovery finished";
}


//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::qDebugDumpSec(int i_fld)
{

    if(!m_bDbOk) return ;
    if(!m_hashIndexDb.contains(i_fld)) return ;
    int ret;
    QVariant qv_pkey;
    Dbc *pC;
    Db *pDb = m_hashIndexDb[i_fld];
    pDb->cursor(NULL, &pC,0);
    Dbt key, pkey, data;
    QVariant qv_key;
    QByteArray qb_buf;
    SLEE_HDB_REC qh_data;
    while(1)
    {
        ret = pC->pget(&key, &pkey, &data, DB_NEXT);
        if(ret) break;
        if(isDbtSpec(pkey))
        {
            qDebug()<<"***zero key";
            continue;
        }

        dbt_to_qv(key, qv_key);

        dbt_to_qb(qb_buf, data);
        qb_to_qh(qb_buf, qh_data);

        dbt_to_qv(pkey, qv_pkey);

        qDebug()<<"indx field"<<i_fld<<"sec key"<<qv_key<<"prim key"<<qv_pkey;//<<"data"<<qh_data;
    }
    pC->close();
}

//------------------------------------------------------------------------------------------------------------

void SleeQHash_BDB::qDebugDumpBase(bool b_dump_duta)
{
    if(!m_bDbOk) return;
    int ret;
    QVariant qv_key;
    SLEE_HDB_REC qh_data;
    QByteArray qb_data;
    Dbt data, key;
    Dbc *pC;
    m_pDb->cursor(NULL, &pC, 0);
    while(1)
    {
        ret = pC->get(&key, &data, DB_NEXT);
        if(ret) break;
        dbt_to_qv(key, qv_key);
        if(isDbtSpec(key))
        {
            qDebug()<<"special key";
            continue;
        }
        dbt_to_qb(qb_data, data);
        qb_to_qh(qb_data, qh_data);
        if(b_dump_duta)
            qDebug()<<qv_key<<qh_data;
        else
            qDebug()<<qv_key<<qh_data;
    }
    pC->close();

}


//------------------------------------------------------------------------------------------------------------

bool SleeQHash_Constrain::process_data(const SLEE_HDB_REC &qh_data)
{
    mqh_data = qh_data;
    m_pos = 0;
    if(do_print) qDebug()<<"__________________parser_start__________________-";
    return process();
}

//------------------------------------------------------------------------------------------------------------

QChar SleeQHash_Constrain::get_char()
{
    QChar ch;
    while(1)
    {
        if(m_pos >= mqstr_process.size()) return QChar('@');
        ch = mqstr_process.at(m_pos);
        m_pos++;
        if(ch == QChar(' ')) continue;
        if(ch == QChar('\t')) continue;
        return ch;
    }

}

//------------------------------------------------------------------------------------------------------------

QVariant SleeQHash_Constrain::get_var()
{
    int num;
    QString qstr_num;
    QChar ch;
    QVariant qv_ret;
    while(1)
    {
        ch = get_char();
        if(ch == '%')
        {
            num = qstr_num.toInt();
            if(mqv_data_lst.size() <= num)
            {
                qCritical()<<"get_var error: index"<<num<<"is out of (constrain data) list size";
                return QVariant();
            }
            qv_ret = mqv_data_lst.at(num);
            return qv_ret;
        }
        if(ch == '#')
        {
            num = qstr_num.toInt();
            if(!mqh_data.contains(num))
            {
                qCritical()<<"get_var error: field"<<num<<"doesn't exists at data";
                return QVariant();
            }
            qv_ret = mqh_data.value(m_flds_lst[num]);
            return qv_ret;
        }
        if(!ch.isDigit())
        {
            qCritical()<<"unexpected symbol:"<<ch;
            return QVariant();
        }
        qstr_num.append(ch);
    }
}

//------------------------------------------------------------------------------------------------------------

bool SleeQHash_Constrain::process(int depth)
{
    QString qstr_tab;
    if(do_print)
    {
        for(int i = 0; i<depth; i++)
            qstr_tab+='\t';
        qDebug()<<qstr_tab<<"process start";
    }
    bool res = true;
    QVariant qv_op1, qv_op2;
    QChar ch;

    bool do_stack = false;
    int op = 0;
    while(1)
    {
        ch = get_char();
        if((ch == '@') ||
           (ch == ')'))
        {
            if(do_print) qDebug()<<qstr_tab<<"process end"<<ch<<"pos"<<m_pos<<"/"<<mqstr_process.size();
            return res;
        }
        if(ch == '(')
        {
            if(do_print) qDebug()<<qstr_tab<<"process:"<<ch;
            res = process(depth+1);
            continue;
        }
        if(ch == '&')
        {
            if(do_print) qDebug()<<qstr_tab<<"process:"<<ch;
            bool res2 = process(depth+1);
            if(do_print) qDebug()<<qstr_tab<<"process"<<res<<"&"<<res2;
            res = (res && res2);
            continue;
        }
        if(ch == '|')
        {
            if(do_print) qDebug()<<qstr_tab<<"process:"<<ch;
            bool res2 = process(depth+1);
            if(do_print) qDebug()<<qstr_tab<<"process:"<<res<<"|"<<res2;
            res = (res || res2);
            continue;
        }
        if(ch == '$')
        {
            qv_op1 = get_var();
            if(do_print) qDebug()<<qstr_tab<<"process: op1"<<qv_op1;
            continue;
        }
        if(ch == '>')
        {
            op = 1;
            do_stack = true;
        }
        if(ch == '<')
        {
            op = -1;
            do_stack = true;
        }
        if(ch == '=')
        {
            op = 0;
            do_stack = true;
        }
        if(do_stack)
        {
            QChar ch = get_char();
            if(ch != '$')
            {
                qCritical()<<qstr_tab<<"process: unexpected symbol"<<ch<<" but _$_ is expected";
                return res;
            }
            qv_op2 = get_var();
            if(do_print) qDebug()<<qstr_tab<<"process: op2"<<qv_op2;
            int cmpr_res = SleeAbstract_BDB::cmpr_qvariants(qv_op1, qv_op2);
            if(do_print) qDebug()<<qstr_tab<<"process: cmpr set"<<op<<"cmpr res"<<cmpr_res;
            if(cmpr_res != op) res = false;
            qv_op1 = qv_op2;
            do_stack = false;
        }
    }
}

//------------------------------------------------------------------------------------------------------------
